package demo.java.lang.management.jvm;

import com.sun.management.GcInfo;
import demo.java.lang.management.vo.GarbageCollectorInfo;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.MemoryUsage;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 收集GC相关信息，如GC次数，耗时等
 * @date 16/1/18
 */
public class GarbageCollector {

    private static final Logger logger = LoggerFactory.getLogger(GarbageCollector.class);

    private static List<String> FULL_GC_NAME_LIST = Arrays.asList("ConcurrentMarkSweep","MarkSweepCompact","PS MarkSweep","G1 Old Generation");
    private final static String MEMORY_USAGE_OLDGEN_KEY = "PS Old Gen";

    private List<GarbageCollectorMXBean> garbageCollectorMXBeans;
    
    private long lastFgcTime=0l;

    public GarbageCollector(List<GarbageCollectorMXBean> garbageCollectorMXBeans) {
        this.garbageCollectorMXBeans = garbageCollectorMXBeans;
    }

    private boolean judgeFullgc(String gcName){
        for(String fullgcName : FULL_GC_NAME_LIST){
            if(StringUtils.equalsIgnoreCase(gcName,fullgcName)){
                return true;
            }
        }
        return false;
    }

    public List<GarbageCollectorInfo> collect() {
        List<GarbageCollectorInfo> garbageCollectorInfos = new ArrayList<GarbageCollectorInfo>();
        try {
            for (GarbageCollectorMXBean collector : garbageCollectorMXBeans) {
                GarbageCollectorInfo collectorInfo = new GarbageCollectorInfo();

                collectorInfo.setCollectionCount(collector.getCollectionCount());
                collectorInfo.setCollectionTime(collector.getCollectionTime());
                collectorInfo.setName(collector.getName());
                
                @SuppressWarnings("restriction")
				GcInfo gcInfo = ((com.sun.management.GarbageCollectorMXBean) collector).getLastGcInfo();
                if (gcInfo!=null && judgeFullgc(collector.getName())) {
					if (lastFgcTime==0) {
						lastFgcTime = gcInfo.getStartTime();
						collectorInfo.setLastFgcDuration(gcInfo.getDuration());
						collectorInfo.setLastFgcTime(gcInfo.getStartTime());
					}else if (gcInfo.getStartTime() > lastFgcTime) {
						lastFgcTime = gcInfo.getStartTime();
						collectorInfo.setLastFgcDuration(gcInfo.getDuration());
						collectorInfo.setLastFgcTime(gcInfo.getStartTime());
					}
                    if(gcInfo.getMemoryUsageBeforeGc()!=null && gcInfo.getMemoryUsageBeforeGc().get(MEMORY_USAGE_OLDGEN_KEY)!=null){
                        MemoryUsage memoryUsage = gcInfo.getMemoryUsageBeforeGc().get(MEMORY_USAGE_OLDGEN_KEY);
                        collectorInfo.setOldBeforeUsed(memoryUsage.getUsed());
                    }
					if(gcInfo.getMemoryUsageAfterGc()!=null && gcInfo.getMemoryUsageAfterGc().get(MEMORY_USAGE_OLDGEN_KEY)!=null){
                        MemoryUsage memoryUsage = gcInfo.getMemoryUsageAfterGc().get(MEMORY_USAGE_OLDGEN_KEY);
                        collectorInfo.setOldAfterUsed(memoryUsage.getUsed());
                    }
				}
                garbageCollectorInfos.add(collectorInfo);
            }
        }catch (Throwable e){
            logger.error("GarbageCollector collect error, {}", e.getMessage());
            return null;
        }

        return garbageCollectorInfos;
    }
}
